﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using DPLAB.IPAMS.DataEntities;

namespace ClassLibrary1
{
    public class IPAMSKernel
    {
        #region Const Value

        private const String cBuyAction = "Buy";
        private const String cDepositAction = "dep"; //deposite

        private const String cDividentActionID = "W07G2_ADiv";
        private const String cSell = "sell";
        private const String cWithdrawalAction = "with"; //withdrawal

        #endregion

        #region Constr'

        public IPAMSKernel(IPAMSDataSet ds)
        {
            IpamsData = ds;
            InitPortfolioBasicDetails();
        }

        #endregion

        #region Private members

        #endregion

        #region Public Access

        public IPAMSDataSet IpamsData { get; set; }

        public String PortfolioID { get; set; }

        public String IHID { get; set; }

        public String PortfolioCurrency { get; set; }

        public String PortfolioType { get; set; }

        public String ManagerID { get; set; }

        public String QuantPortfolio { get; set; }

        #endregion

        #region Private Methods

        #region Util Methods

        private String GetPaperCurrency(String PaperId)
        {
            IEnumerable<String> CurrencyList = from paper in IpamsData.Paper
                                               from country in IpamsData.Country
                                               where country.Code.Equals(paper.CountryCode)
                                               select country.CurrencyCode;

            return CurrencyList.First();
        }

        private Decimal GetExchenageRate(String Source, String Dest, DateTime date)
        {
            if (Source.Equals(Dest))
                return 1;

            IEnumerable<Decimal> ExchangeRateList =
                from exhanges in IpamsData.ExchangeRatesQuote
                where exhanges.SourceCurrencyCode.Equals(Source) &&
                      exhanges.Date.Equals(date) &&
                      exhanges.TargetCurrencyCode.Equals(Dest)
                select exhanges.Rate;

            Decimal ExchangeRate;

            // TODO - dont need to do this check, since assum all data in avl'
            if (ExchangeRateList.Count() == 0)
            {
                ExchangeRateList =
                    from exhanges in IpamsData.ExchangeRatesQuote
                    where exhanges.TargetCurrencyCode == Source &&
                          exhanges.Date.Equals(date) &&
                          exhanges.SourceCurrencyCode == Dest
                    select exhanges.Rate;

                if (ExchangeRateList.Count() == 0)
                {
                    // TODO - that mean that we don;t have that info.
                    // At this point let assum that there is no such problem
                }

                ExchangeRate = 1/ExchangeRateList.First();
            }
            else
                ExchangeRate = ExchangeRateList.First();


            return ExchangeRate;
        }

        private void CalculateCreditAndDebitForTransactionRow(
            IPAMSDataSet.TransactionRow TransactionRow,
            Boolean Debit)
        {
            Decimal Original_CurrencyTotal = Decimal.Parse(TransactionRow.Original_CurrencyPricePerUnit)*
                                             TransactionRow.Quantity;
            Decimal PortfolioCurrencyTotal = Original_CurrencyTotal*
                                             Decimal.Parse(TransactionRow.OriginalCurrencyPortfolioCurrencyExchangeRate);
            if (Debit)
            {
                TransactionRow.Original_CurrencyTotalCredit = 0.ToString();
                TransactionRow.PortfolioCurrencyTotalCredit = 0.ToString();

                TransactionRow.Original_CurrencyTotalDebit = Original_CurrencyTotal.ToString();
                TransactionRow.PortfolioCurrencyTotalDebit = PortfolioCurrencyTotal.ToString();
            }
            else
            {
                TransactionRow.Original_CurrencyTotalCredit = Original_CurrencyTotal.ToString();
                TransactionRow.PortfolioCurrencyTotalCredit = PortfolioCurrencyTotal.ToString();

                TransactionRow.Original_CurrencyTotalDebit = 0.ToString();
                TransactionRow.PortfolioCurrencyTotalDebit = 0.ToString();
            }
        }

        #endregion

        #region Init Methods to TransactionRow

        private void InitPortfolioBasicDetails()
        {
            PortfolioID = IpamsData.Portfolio[0].ID;
            IHID = IpamsData.Portfolio[0].InvestmentHouseID;
            ManagerID = IpamsData.Portfolio[0].ManagerId;
            PortfolioType = IpamsData.Portfolio[0].TypeID;
            PortfolioCurrency = IpamsData.Portfolio[0].CurrencyCode;
            QuantPortfolio = IpamsData.Portfolio[0].QuantityPortfolioID;
        }

        private void InitTransactionRowBasicDetails(
            IPAMSDataSet.TransactionRow TransactionRow)
        {
            TransactionRow.PortfolioID = PortfolioID;
            TransactionRow.IHID = IHID;
            TransactionRow.ManagerID = ManagerID;
            TransactionRow.Portfolio_Type = PortfolioType;
            TransactionRow.PortfolioCurrency = PortfolioCurrency;
        }

        private void InitTransactionRowFromQuantityTransaction(
            IPAMSDataSet.QuantityTransactionRow QuantityTransactionRow,
            IPAMSDataSet.TransactionRow TransactionRow)
        {
            TransactionRow.PaperID = QuantityTransactionRow.PaperID;
            TransactionRow.ActionID = QuantityTransactionRow.ActionID;
            TransactionRow.ValueDate = QuantityTransactionRow.ValueDate;
            TransactionRow.Quantity = QuantityTransactionRow.Quantity;
            TransactionRow.PackageID = QuantityTransactionRow.PackageID;
            TransactionRow.TransactionID = QuantityTransactionRow.TransactionID;
        }

        private void InitTransactionRowFromCashTransaction(
            IPAMSDataSet.CashTransactionRow CashTransactionRow,
            IPAMSDataSet.TransactionRow TransactionRow)
        {
            String CurrencyCode = CashTransactionRow.CurrencyCode;
            Decimal ExchangeRate = GetExchenageRate(CurrencyCode, TransactionRow.PortfolioCurrency,
                                                    CashTransactionRow.DateTime);
            Boolean Debit = CashTransactionRow.ActionID.Contains(cWithdrawalAction);

            TransactionRow.ActionID = CashTransactionRow.ActionID;
            TransactionRow.ValueDate = CashTransactionRow.DateTime;
            TransactionRow.Quantity = CashTransactionRow.Amount;
            TransactionRow.TransactionID = CashTransactionRow.TransactionID;
            TransactionRow.OriginalCurrency = CurrencyCode;
            TransactionRow.Original_CurrencyPricePerUnit = "1";
            TransactionRow.OriginalCurrencyPortfolioCurrencyExchangeRate = ExchangeRate.ToString();

            CalculateCreditAndDebitForTransactionRow(TransactionRow, Debit);
        }

        private void InitPapaerDetailsInsideTransactionRow(
            IPAMSDataSet.QuantityTransactionRow SourceQuantityRow,
            IPAMSDataSet.TransactionRow DestTransactionRow)
        {
            IEnumerable<String> CurrencyCodeList =
                from paper in IpamsData.Paper
                from country in IpamsData.Country
                where paper.ID.Equals(SourceQuantityRow.PaperID) && country.Code.Equals(paper.CountryCode)
                select country.CurrencyCode;
            String CurrencyCode = CurrencyCodeList.First();

            IEnumerable<Decimal> PriceList =
                from quates in IpamsData.StocksQuote
                where
                    quates.PaperID.Equals(SourceQuantityRow.PaperID) && quates.Date.Equals(SourceQuantityRow.ValueDate)
                select quates.ClosingRate;
            Decimal Price = PriceList.First();

            Boolean Debit = SourceQuantityRow.ActionID.Contains(cBuyAction);

            Decimal ExchangeRate = GetExchenageRate(CurrencyCode, DestTransactionRow.PortfolioCurrency,
                                                    SourceQuantityRow.ValueDate);

            // Since asuum only 1 
            DestTransactionRow.OriginalCurrency = CurrencyCode;
            DestTransactionRow.Quantity = SourceQuantityRow.Quantity;
            DestTransactionRow.Original_CurrencyPricePerUnit = Price.ToString();
            DestTransactionRow.OriginalCurrencyPortfolioCurrencyExchangeRate = ExchangeRate.ToString();

            // The balance is calc' in the end ....
            CalculateCreditAndDebitForTransactionRow(DestTransactionRow, Debit);
        }


        private void InitTransactionRowFromTransactionRow(
            IPAMSDataSet.TransactionRow SourceRow,
            IPAMSDataSet.TransactionRow DestRow)
        {
            InitTransactionRowBasicDetails(DestRow);

            DestRow.PackageID = SourceRow.PackageID;
            DestRow.TransactionID = SourceRow.TransactionID;
            DestRow.PaperID = SourceRow.PaperID;
            DestRow.OriginalCurrency = SourceRow.PortfolioCurrency;
        }

        #endregion

        #region Add data to DB Method

        private void AddStockAction()
        {
            IPAMSDataSet.TransactionRow TransactionRow = IpamsData.Transaction.NewTransactionRow();
            // At this moment assum that all action inside Quantity Transaction are
            // action done on stock's\papers

            foreach (IPAMSDataSet.QuantityTransactionRow QuantityRow in IpamsData.QuantityTransaction)
            {
                InitTransactionRowBasicDetails(TransactionRow);
                InitTransactionRowFromQuantityTransaction(QuantityRow, TransactionRow);
                InitPapaerDetailsInsideTransactionRow(QuantityRow, TransactionRow);

                IpamsData.Transaction.AddTransactionRow(TransactionRow);
            }
        }

        private void AddCashAction()
        {
            foreach (IPAMSDataSet.CashTransactionRow CashRow in IpamsData.CashTransaction)
            {
                IPAMSDataSet.TransactionRow TransactionRow = IpamsData.Transaction.NewTransactionRow();
                InitTransactionRowBasicDetails(TransactionRow);
                InitTransactionRowFromCashTransaction(CashRow, TransactionRow);

                IpamsData.Transaction.AddTransactionRow(TransactionRow);
            }
        }

        private void AddTransaction2QuantityTransactions(
            IEnumerable<IPAMSDataSet.TransactionRow> tranData)
        {
            foreach (IPAMSDataSet.TransactionRow row in tranData)
            {
                IPAMSDataSet.QuantityTransactionRow QuntiRow = IpamsData.QuantityTransaction.NewQuantityTransactionRow();

                QuntiRow.QuantityPortfolioID = row.PortfolioID;
                QuntiRow.PackageID = row.PackageID;
                QuntiRow.TransactionID = row.TransactionID;
                QuntiRow.PaperID = row.PaperID;
                QuntiRow.ActionID = row.ActionID;
                QuntiRow.ValueDate = row.ValueDate;
                QuntiRow.Quantity = row.Quantity;
                QuntiRow.PackageID = row.PackageID;
                QuntiRow.QuantityPortfolioID = QuantPortfolio;

                IpamsData.QuantityTransaction.AddQuantityTransactionRow(QuntiRow);
            }
        }

        #endregion

        #region Calculare methods

        private void CalculatePeriodCommissions()
        {
        }

        private void CalculateDiviedentForPaper(IPAMSPaperTimeLine PaperInfo,
                                                IPAMSDataSet.TransactionDataTable tmpTransaction)
        {
            IEnumerable<IPAMSDataSet.DividendsQuoteRow> selectRow = from divident in IpamsData.DividendsQuote
                                                                    where PaperInfo.PaperId.Equals(divident.PaperID) &&
                                                                          divident.ExDate > PaperInfo.BuyDate &&
                                                                          divident.ExDate < PaperInfo.SaleDate
                                                                    select divident;

            foreach (IPAMSDataSet.DividendsQuoteRow divRow in selectRow)
            {
                IPAMSDataSet.TransactionRow transactionRow = tmpTransaction.NewTransactionRow();

                InitTransactionRowBasicDetails(transactionRow);

                transactionRow.PackageID = divRow.PackageID;
                transactionRow.TransactionID = divRow.TransactionID;
                transactionRow.PaperID = divRow.PaperID;
                transactionRow.ValueDate = divRow.ExDate;
                transactionRow.Quantity = PaperInfo.Quantity;
                transactionRow.OriginalCurrency = GetPaperCurrency(divRow.PackageID);
                transactionRow.Original_CurrencyPricePerUnit = divRow.Value.ToString();
                ///
                /// TODO may want to move this to the "CalculateCreditAndDebitForTransactionRow" Method
                ///
                transactionRow.OriginalCurrencyPortfolioCurrencyExchangeRate = (GetExchenageRate(
                    transactionRow.OriginalCurrency, transactionRow.PortfolioCurrency, divRow.ExDate)).ToString();

                ///
                /// TODO - at the moment tleave it like that ....
                ///
                transactionRow.ActionID = cDividentActionID;

                CalculateCreditAndDebitForTransactionRow(transactionRow, false);


                tmpTransaction.AddTransactionRow(transactionRow);
            }
        }

        private void CalculateDiviedent()
        {
            var tmpTransaction = new IPAMSDataSet.TransactionDataTable();
            var timeLine = new List<IPAMSPaperTimeLine>();

            IEnumerable<IPAMSDataSet.QuantityTransactionRow> sortedRow = from quantity in IpamsData.QuantityTransaction
                                                                         orderby quantity.ValueDate
                                                                         select quantity;

            //Create very simplt paper time line
            ///
            /// TODO  - need to finish here - to think about all the other case's
            ///
            foreach (IPAMSDataSet.QuantityTransactionRow row in sortedRow)
            {
                int i;
                if (row.ActionID.Contains(cBuyAction))
                    timeLine.Add(new IPAMSPaperTimeLine(row.PaperID, row.ValueDate, DateTime.Parse("01/01/1900"),
                                                        row.Quantity));

                if (row.ActionID.Contains(cSell))
                {
                    for (i = 0; i < timeLine.Count; i++)
                        if (timeLine[i].PaperId.Equals(row.PaperID) && timeLine[i].BuyDate > timeLine[i].SaleDate)
                            break;

                    timeLine[i].SaleDate = row.ValueDate;
                }
            }

            foreach (IPAMSPaperTimeLine PaperInfo in timeLine)
            {
                if (PaperInfo.BuyDate > PaperInfo.SaleDate)
                {
                    ///
                    /// TODO - will have to change that  - plug in the downloader ....
                    ///
                    PaperInfo.SaleDate = DateTime.Parse("01/01/2008");
                }
                CalculateDiviedentForPaper(PaperInfo, tmpTransaction);
            }

            IpamsData.Transaction.Merge(tmpTransaction);
        }

        private void CalculateCommissions()
        {
            CalculateActionCommissions();
            CalculatePeriodCommissions();
        }

        private void CalculateActionCommissionsFromTransaction(IPAMSDataSet.TransactionRow SourceTransaction,
                                                               IPAMSDataSet.TransactionDataTable tmpTransaction)
        {
            IEnumerable<IPAMSDataSet.CommissionRow> selectRow = from commision in IpamsData.Commission
                                                                where
                                                                    SourceTransaction.ActionID.Equals(commision.ActionID) &&
                                                                    SourceTransaction.ValueDate >
                                                                    commision.ValidFromDate &&
                                                                    SourceTransaction.ValueDate < commision.ValidToDate
                                                                select commision;

            foreach (IPAMSDataSet.CommissionRow row in selectRow)
            {
                IPAMSDataSet.TransactionRow transactionRow = tmpTransaction.NewTransactionRow();

                InitTransactionRowFromTransactionRow(SourceTransaction, transactionRow);

                ///
                /// TODO - need to change the schema here  to be row.SourceID -> row.PackageId
                /// Also need to determent - which Package ID to use ? from the transaction or from the commision ...
                ///

                transactionRow.ActionID = row.ID;
                transactionRow.ValueDate = SourceTransaction.ValueDate;

                ///
                /// need to be carefull here , since the transaction can be in currency A the commision in currency B
                /// and the portfolio currency in currency C !
                ///

                ///
                /// First move the Source Trnsaction to the commision Currency
                ///
                Decimal exchageRate = GetExchenageRate(SourceTransaction.OriginalCurrency,
                                                       row.CurrencyCode, SourceTransaction.ValueDate);

                ///
                /// TODO need to change Quantity to Decimal
                ///
                Decimal quantity = exchageRate*SourceTransaction.Quantity;

                ///
                /// This transaction will be in the commision CurrencyCode
                ///
                transactionRow.Quantity = (int) quantity;
                transactionRow.OriginalCurrency = row.CurrencyCode;

                Decimal Min, Max, Rate, Debit;
                Min = row.Min;
                Max = row.Max;
                Rate = row.Rate;

                if (quantity*Rate < Min)
                    Debit = Min;
                else
                {
                    if (quantity*Rate > Max)
                        Debit = Max;
                    else
                        Debit = quantity*Rate;
                }

                ///
                /// Assum that commision are onlt Debit :-)
                ///
                transactionRow.Original_CurrencyPricePerUnit = Rate.ToString();
                transactionRow.Original_CurrencyTotalDebit = Debit.ToString();
                transactionRow.Original_CurrencyTotalCredit = "0";

                exchageRate = GetExchenageRate(row.CurrencyCode, SourceTransaction.PortfolioCurrency,
                                               SourceTransaction.ValueDate);

                transactionRow.PortfolioCurrency = SourceTransaction.PortfolioCurrency;
                transactionRow.OriginalCurrencyPortfolioCurrencyExchangeRate = exchageRate.ToString();
                transactionRow.PortfolioCurrencyTotalDebit = (Debit*exchageRate).ToString();
                transactionRow.PortfolioCurrencyTotalCredit = "0";

                transactionRow.TransactionID = SourceTransaction.TransactionID;

                ///
                /// TODO - may not need tmpTransaction in this method ....
                ///
                tmpTransaction.AddTransactionRow(transactionRow);
            }
        }

        private void CalculateActionCommissions()
        {
            var tmpTransaction = new IPAMSDataSet.TransactionDataTable();
            IEnumerable<IPAMSDataSet.TransactionRow> selectRow = from transaction in IpamsData.Transaction
                                                                 from commision in IpamsData.Commission
                                                                 where
                                                                     transaction.ActionID.Equals(commision.ActionID) &&
                                                                     transaction.ValueDate > commision.ValidFromDate &&
                                                                     transaction.ValueDate < commision.ValidToDate
                                                                 select transaction;


            foreach (IPAMSDataSet.TransactionRow row in selectRow)
            {
                CalculateActionCommissionsFromTransaction(row, tmpTransaction);
            }

            IpamsData.Transaction.Merge(tmpTransaction);
        }

        private void CalculateTaxesOnAction(IPAMSDataSet.TransactionRow SourceTransaction,
                                            IPAMSDataSet.TransactionDataTable tmpTransaction)
        {
            IEnumerable<IPAMSDataSet.TaxRow> selectedRows = from tax in IpamsData.Tax
                                                            where tax.ActionId.Equals(SourceTransaction.ActionID) &&
                                                                  tax.ValidFromDate < SourceTransaction.ValueDate &&
                                                                  tax.ValidToDate > SourceTransaction.ValueDate
                                                            select tax;
            // Assum that all Tax are Debit  :-)
            // do it in the protfolio current
            // one can pay more then 1 tax on 1 action
            foreach (IPAMSDataSet.TaxRow taxRow in selectedRows)
            {
                IPAMSDataSet.TransactionRow transactionRow = tmpTransaction.NewTransactionRow();
                Decimal rate = taxRow.Rate;

                InitTransactionRowFromTransactionRow(SourceTransaction, transactionRow);

                transactionRow.ActionID = taxRow.ID;
                transactionRow.ValueDate = SourceTransaction.ValueDate;
                ///
                /// TODO Need to change Quantity to Decimal
                ///
                transactionRow.Quantity =
                    Convert.ToInt32(Convert.ToDecimal(SourceTransaction.PortfolioCurrencyTotalCredit) == 0
                                        ?
                                            Convert.ToDecimal(SourceTransaction.PortfolioCurrencyTotalDebit)
                                        : Convert.ToDecimal(SourceTransaction.PortfolioCurrencyTotalCredit));

                transactionRow.Original_CurrencyPricePerUnit = rate.ToString();
                transactionRow.OriginalCurrencyPortfolioCurrencyExchangeRate = "1";

                transactionRow.PortfolioCurrencyTotalDebit = transactionRow.Original_CurrencyTotalDebit =
                                                             (rate*transactionRow.Quantity).ToString();
                transactionRow.PortfolioCurrencyTotalCredit = transactionRow.Original_CurrencyTotalCredit = "0";


                tmpTransaction.AddTransactionRow(transactionRow);
            }
        }

        private void CalculateTaxes()
        {
            var tmpTransaction = new IPAMSDataSet.TransactionDataTable();

            IEnumerable<IPAMSDataSet.TransactionRow> selectedRows = from transaction in IpamsData.Transaction
                                                                    from tax in IpamsData.Tax
                                                                    where tax.ActionId.Equals(transaction.ActionID) &&
                                                                          tax.ValidFromDate < transaction.ValueDate &&
                                                                          tax.ValidToDate > transaction.ValueDate
                                                                    select transaction;

            foreach (IPAMSDataSet.TransactionRow transactionRow in selectedRows)
            {
                CalculateTaxesOnAction(transactionRow, tmpTransaction);
            }

            IpamsData.Transaction.Merge(tmpTransaction);
        }

        #endregion

        #endregion

        #region Public Methods

        public void QuantityTransactions2Transactions()
        {
            //Clear all pre data
            IpamsData.Transaction.Clear();


            ///
            /// The order in which we do those action is importence 
            ///
            AddStockAction();
            AddCashAction();
            CalculateDiviedent();
            CalculateCommissions();
            CalculateTaxes();
        }

        public void Transactions2QuantityTransactions()
        {
            //Clear all pre data
            IpamsData.QuantityTransaction.Clear();

            var tranData = new IPAMSDataSet.TransactionDataTable();

            IEnumerable<IPAMSDataSet.TransactionRow> selectedRows = from transaction in IpamsData.Transaction
                                                                    from action in
                                                                        IpamsData.ActionForQuantityTransactions
                                                                    where action.ID == transaction.ActionID
                                                                    select transaction;

            AddTransaction2QuantityTransactions(selectedRows);
        }

        #endregion
    }
}